/***************************************************************************
													parser.cpp  -  description
														 -------------------
		begin                : Mon May 6 2002
		copyright            : (C) 2002 by Michiel Visser
		email                : 
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
/*
level only applies to expressions!

	. (binary) -> 
	(...) [...] (binary) [:...] !(postfix)
	.? * & ! + - @ @@ (unary)
	* / % @ (binary)
	+ - 
	<< >> :+ :-
	< <= >= >
	== !=
	&
	^
	|
	&&
	||
	? : (N-ary)
	~ ~: ~%
	|...|
	=!> => =>> =>>> ? (unary)
	= += -= *= /= %= <<= >>= +&lt;= :+= :-=
	<~ <!-
	,

levels: 
	Expr(chain, function)
	
	assign/modify
	switch
	...
	mod
	div
	mul
	unary
	index
	dot
	
Program ::= StatementList
StatementList ::= SubStatement ';' StatementList?
SubStatement ::=  Forwarder | CatchBlock
CatchBlock ::= TopStatement | CatchBlock ( '<~' |'<!~' ) Function
Function ::= Parameters? TopStatement
MapList ::= MapElem (',' MapList)?
ExprList ::= Expr (',' ExprList)?
MapElem ::= Expr | Expr ':' Expr
ArgumentList ::= Expr (',' ArgumentList)?
TopStatement ::= '=!>' Expr | Expr | MultiStat
Expr::= FunctionLiteral | ChainBlock | LValue ( '=' | '+=' ) Expr
ChainBlock ::= (MultiStat | Switch ) ( '~' | '~:' |'~%' ) Function
Switch ::= Binary ('?' TopStatement : ...)
Parameters ::= '|' ParameterList '|'
ParameterList ::= Parameter (',' ParameterList)?
Parameter ::= TypeDef? Identifier
Binary ::= Unary
Unary ::= Index
Index ::= Operand ('[' Expr ']' | Arguments )? 
Operand ::= TypeSelection ? ( SubExpression | String | Number | Var | Hash)
SubExpression ::= List | Map | Sequence
Forwarder ::= '=>' Expr? | '=>>' Expr? | '=>>>' Expr?
FunctionLiteral ::= '?' Function
Arguments ::= '(' ArgumentList ')'
MultiStat ::= '{' StatementList '}'
Sequence ::= '(' ExprList ')'
Map ::= '{' MapList '}'
List ::= '[' ExprList ']'

toplevel=0
(a + b) * (c + d)
        higher-level
every toList returns two codes: one for the left side and one for the right side. those determine if brackets are needed
if unary, then special: 
(? 4 + 3) + 5 
5 + ? 4 + 3
(5 + ? 4 + 3) + 6

ChainBlockOrMultiStat ::= Chain | MultiStat
ExprOrMultiStat ::= ...

  
LValue ::= '*' LValue | Var Indexing? TODO: ook (x@+=3)=4
Indexing ::= '[' Expr ']' | '[:

===old:===

TyleParser
	Sequence
		Assign(=Expr)(RR)
			Modify (=?, .?)
				Chain(RR)
					Param (<...>...)
						Switch(RR) (=while,switch)
							Binary
								Unary
									Index
										Operand
											Block
												{ finishList }
													Assign
												( finishSequence )
													Sequence
														Assign
												{: finishMap }
													finishMap
											String
											Number
											Var
*/
 
#include "parser.h"
#include "regexp.h"

#include<math.h>

using namespace Expression;

static const int DYNAMIC_BITMASK_DYN = 1;
static const int DYNAMIC_BITMASK_SHAREDLITERAL = 2;
static const int DYNAMIC_BITMASK_REF = 4;

inline bool isIdentifier(char c)
{
	return (c>='a'&&c<='z') || (c>='A'&&c<='Z') || c=='_';
}

TyleParser::TyleParser(const unsigned char *pos, EPtr &result, void *returnObjectSpace):
	m_pos(pos),
	m_c(0),
	m_line(1),
	m_s(0x100000)
{
	session.m_funcLevel=0;
	try {
		nextChar();
		nextWord();
		result = parseProgram();
		_ASSERT(!!result);
		constify(result);
		consolidateSharedLiterals(result);
		skipSpaces();
		if (m_s[0]!='\0') {
			throw("Could not parse completely.");
		}
	}
	catch (const char *error) { 
		if (returnObjectSpace)
		{	char s[256];
	 		sprintf(s,"Parse error at line %i: %s",m_line, (char *)error);
			_TVAR(s,s);
	 		new (returnObjectSpace) StringObject(s);
		}
		result=EPtr();
	}
}

void TyleParser::nextChar(){ 
	while (m_pos[0]=='/')
	{ if (m_pos[1]=='*')
		{ for (m_pos+=2; *m_pos!=0; m_pos++)
			{ if (*m_pos==10) m_line++;
				if (*m_pos=='*' && m_pos[1]=='/') { m_pos+=2; break; }
			}
		} else if (m_pos[1]=='/')
		{ for (m_pos+=2; *m_pos!=0; m_pos++)
			{ if (*m_pos==10) { m_line++; m_pos++; break; }
			}
		} else break;
	}
	m_c=*(m_pos++);
	if (m_c==10) m_line++;
}

void TyleParser::nextWord() { 
	int pos = 0;
#define SCP _MACRO_BEGIN storeChar(pos); nextChar(); _MACRO_END
#define SRCP _MACRO_BEGIN storeChar(pos); nextRawChar(); _MACRO_END
	skipSpaces();
	if (m_c=='\"')
	{ do
		{ if (m_c==0) throw("String not finished.");
			else if (m_c<32) { nextRawChar(); continue; }
			else if (m_c=='\\')
			{	nextRawChar();
			m_c=parseEscapeChar(m_c,m_pos);
			}
			SRCP;
		} while (m_c!='\"');
		m_strlen=pos-1;
		nextChar();
	}
	else if (m_c=='\'')
	{	m_c='\"';
	  do
		{ if (m_c==0) throw("String not finished.");
			SRCP;
		} while (m_c!='\'');
		m_strlen=pos-1;
		nextChar();
	}
	else if (m_c=='`')
	{ do
		{ if (m_c==0) throw("Regexp not finished.");
			else if (m_c<32) { nextRawChar(); continue; }
			else if (m_c=='\\')
			{	SRCP;
				if (m_c==0) throw("Escape sequence not finished.");
			}
			SRCP;
		} while (m_c!='`');
		m_strlen=pos-1;
		unsigned char mask=0;
		for(;;)
		{ nextRawChar();
			if      (m_c=='i') mask|=1;
			else if (m_c=='m') mask|=2;
			else if (m_c=='o') mask|=4;
			else break;
		}
		m_s[pos++]=mask;
	}
	else if (m_c>' ')
	{ short omc=m_c;
		SCP;
		switch(omc)
		{
		case '#': if (m_c>='0' && m_c<='9') do { SCP; } while (m_c>='0' && m_c<='9'); else while(m_c!=';' && m_c!=',' && m_c!=0) SCP; break;
		case '.': if (m_c=='*'||m_c=='@'||m_c=='?') SCP; else if (m_c=='.' && m_pos[0]=='.') {SCP; SCP;} break; 
		case '~': if (m_c==':'||m_c=='%'||m_c=='>') SCP; break; 
		case '|': if (m_c=='|') SCP;           if (m_c=='=') SCP; break;
		case '&': if (m_c=='&') SCP;           if (m_c=='=') SCP; break;
		case '+': if (m_c=='<'||m_c=='>') SCP; if (m_c=='=') SCP; break;
		case '<': if (m_c=='>') SCP; else { if (m_c=='<') { SCP; if (m_c=='=') SCP; } else if (m_c=='=') SCP; else if ((m_c=='!'||m_c=='~') && m_pos[0]=='~') {SCP; SCP; } } break;
		case '>': if (m_c=='>') SCP;           if (m_c=='=') SCP; break;
		case '-': if (m_c=='>') SCP; else { if (m_c=='*') SCP; if (m_c=='=') SCP; break; }
		case '%': if (m_c=='=') SCP; break;
		case '$': if (m_c=='$') SCP; else if (m_c=='#') SCP; else if (m_c=='.' && m_pos[0]=='.' && m_pos[1]=='.') {SCP; SCP; SCP; } break;
		case '@': if (m_c=='@') SCP; else if (m_c=='+'||m_c=='-') { SCP; if (m_c=='=') SCP; } break;
		case '*': if (m_c=='!') { SCP; break; } // else fall through
		case '^':
		case '/': if (m_c=='=') SCP; break;
		case '[': if (m_c==':') SCP; break;
		case '=': if (m_c=='=' ) SCP; else if (m_c=='!' && m_pos[0]=='>') {SCP; SCP;} else { if (m_c=='>' ) SCP; if (m_c=='>' ) SCP; if (m_c=='>' ) SCP; } break;
		case '!': if (m_c=='=' && m_pos[0]!='=' && m_pos[0]!='>') SCP; break;
		default:
			if ( omc>='0' && omc<='9')
			{	if (omc=='0' && (m_c=='x'||m_c=='X'))
				{	do { SCP; } while ((m_c>='0' && m_c<='9') || (m_c>='a' && m_c<='f') || (m_c>='A' && m_c<='F'));
				}
				else if (omc=='0' && (m_c=='b'||m_c=='B'))
				{	do { SCP; } while ((m_c=='0' || m_c=='1'));
				}
				else if (omc=='0' && (m_c=='o'||m_c=='O'))
				{	do { SCP; } while ((m_c>='0' && m_c<='7'));
				}
				else
				{	while (m_c>='0' && m_c<='9') SCP;
					if (m_c=='.' && m_pos[0]>='0' && m_pos[0]<='9')
					{	SCP; 
						do { SCP; } while (m_c>='0' && m_c<='9');
					}
					if (m_c=='e'||m_c=='E')
					{	SCP;
						if (m_c=='-'||m_c=='+') SCP;
						while (m_c>='0' && m_c<='9') SCP;
					}
				}
			}
			else if ( isIdentifier(omc) )
			{ while ((m_c>='0' && m_c<='9') || isIdentifier(m_c) ) SCP;
			}
			break;
		}
	}
	m_s[pos]=0;
#undef SCP
}

bool TyleParser::parseWord(const char *word)
{
	if (strcmp(m_s,word)!=0)
	{ return false;
	}
	nextWord();
	return true;
}

EPtr TyleParser::parseString() {
	if (m_s[0]!='\"' && m_s[0]!='`') return EPtr();
	{
		EPtr res;
		if (m_s[0]=='`')
		{ (NEW_CONSTEXPR(res) RegExpObject(((unsigned char *)m_s)+1,m_strlen,((unsigned char *)m_s)[m_strlen+1]))->parse();
		}
		else
		{ NEW_CONSTEXPR(res) StringObject(((char *)m_s)+1,m_strlen);
		}
		nextWord();
		return res;
		}
}

void TyleParser::addStructElem(List<StructElem> &ses, TypeVar parent, String name, TypeVar type, TypeFunc *func)
{
	int i=ses.length();
	new (ses.end()) StructElem(name, type);
	if (func)
	{	new (*structing.funcs) TypeToStructElemAssignment (parent, i, func);
		structing.funcs++;
	}
}

TypeVar TyleParser::parseType(/*out*/ TypeFunc *&funcOut)
{
	TypeVar p(parseStructConcat(funcOut));

	if (parseWord(":"))
	{ 
		TypeVar np;
		new (np) TypeVal();
		if (!!funcOut)
		{ new (*structing.funcs) TypeToStructElemAssignment (np, 0, funcOut);
			structing.funcs++;
		}
		TypeVar p2(parseType(funcOut));
		if (!!funcOut)
		{ new (*structing.funcs) TypeToStructElemAssignment (np, -1, funcOut);
			structing.funcs++;
			funcOut=0;
		}
		List<StructElem> elems;
		new (elems) StructElem(String(), p);
		np->set(elems, false);
		np->m_childType=p2;
		return np;
	}
	return p;
}
TypeVar TyleParser::parseStructConcat(/*out*/ TypeFunc *&funcOut)
{
	TypeVar p(parseTypeAssignment(funcOut));
	if (parseWord("+"))
	{
		if (!funcOut)
		{	funcOut = new TypeSelection(p);
			p = TypeVar();
		}
		do 
		{	TypeFunc *func2;
			TypeVar p2(parseTypeAssignment(func2));
			if (!func2)
			{	func2 = new TypeSelection(p2);
			}
			funcOut = new StructConcat(funcOut,func2);
		} while (parseWord("+"));
	}
	return p;
}

TypeVar TyleParser::parseTypeLeftVal(/*out*/ TypeFunc *&funcOut)
{
	funcOut=0;
	TypeVar p;
	String name((char *)m_s);	
	nextWord();
	p = session.findType(name);
	if (!p) {
		funcOut = new PreTypeSelection(name);
		//old!-> todo p = session.newTypeEntry(name);
	}
	if (parseWord(".")) { 
		if (!!p)
			funcOut = new TypeSelection(p);
		p=TypeVar();
		do
		{	String name((char *)m_s);	//todo: add word check
			nextWord();
			funcOut=new StructIndexing(funcOut,name);
		} while (parseWord("."));
	}
	return p;
}
TypeVar TyleParser::parseTypeAssignment(/*out*/ TypeFunc *&funcOut)
{
	funcOut=0;
	if ( isIdentifier(m_s[0]))
	{ TypeVar p = parseTypeLeftVal(funcOut);
		if (parseWord("="))
		{	TypeFunc *func2;
			if (!funcOut)
				funcOut = new TypeSelection(p); 
			TypeVar p2 (parseType(func2));
			if (!!p2)
				func2 = new TypeSelection(p2); 
/*			if (!!p)
			{	new (*structing.funcs) TypeAssignment(funcOut, func2);
				funcOut=0;
			}
			else
				funcOut = new TypeAssignment(funcOut, func2);*/
			funcOut = new TypeAssignment(funcOut, func2);
			return TypeVar();
		}
		return p;
	}
	else return parseTypeLiteral(funcOut);
}

TypeVar TyleParser::parseTypeLiteral(TypeFunc *&funcOut)
{
	funcOut = 0;
	TypeVar p;
	List<StructElem> ses;
	if (parseWord("<"))
	{	bool allowDynamicElems=false;
		new (p) TypeVal(allowDynamicElems);
		while (!parseWord(">"))
		{	
			if (parseWord("..."))
			{ allowDynamicElems=true;
			  if (!parseWord("]")) throw("Expected '}' after '...'");
			  break;
		  }
	  	TypeFunc *subFunc;
			TypeVar subType(parseTypeSelection(subFunc));
	  	if (!isIdentifier(m_s[0])) throw("Expected identifier.");
			String m_sc((char *)m_s);
			addStructElem(ses,p,m_sc,subType,subFunc);
	  	nextWord();
	  	if (parseWord(">")) break;
	  	if (!parseWord(","))
	  	{ throw("Expected ',' or ']'.");
	  	}
		}
		p->set(ses,allowDynamicElems);
	}
	else if (parseWord(":"))
	{ p=parseType(funcOut);
		TypeVar np;
		new (np) TypeVal(p);
		if (!!funcOut)
		{ new (*structing.funcs) TypeToStructElemAssignment (np, -1, funcOut);
			structing.funcs++;
			funcOut=0;
		}
		return np;
	}
	else if (parseWord("("))
	{ p=parseType(funcOut);
		if (!parseWord(")"))
		{ throw("Expected ')'.");
		}    
	}
	return p;
}


TypeVar TyleParser::parseTypeSelection(TypeFunc *&funcOut, bool *pFound)
{
	funcOut=0;
	TypeVar t;
	bool found = parseWord("<");
	if (pFound)
		*pFound = found;
	if (found) {
		t=parseType(funcOut);
		if (!parseWord(">")) throw("Missing '>' of Type definition.");
	}
	return t;
}
/*
static String concatStrings(List<String> &txt)
{
	int l=0;
	FOREACH(List<String>,txt,i) l+=i->length();
	String str(l+1);
	char *sp=str;
	FOREACH(List<String>,txt,i)
	{ int l=i->length();
		memcpy(sp,(char *)*i,l);
		sp+=l;
	}
	*sp=' ';
	return str;
}*/
EPtr TyleParser::parseTypeSelectionExpr() {
	EPtr res;
	List<TypeFunc> structFuncs;
	structing.funcs=&structFuncs;
	TypeFunc *func;
	bool found;
	TypeVar type(parseTypeSelection(func,&found));
	if (!!type || (!func && found))
	{	func = new TypeSelection(type);
	}
	if(func)
	{ new (res) TypeSelectionExpr(func,structFuncs);
	}
	return res;
}

EPtr TyleParser::getArgumentListExpr() {
	EPtr res;
	if (session.m_funcLevel<4)
		res = session.m_argumentListExprs[session.m_funcLevel];
	else
		new (res) ArgumentListExpr(session.m_funcLevel);
	return res;
}

EPtr TyleParser::parseVar() {
	EPtr res;
	int i;
	if (m_s[0]=='$') {
		if (parseWord("$")) {
			if (session.m_funcLevel==0)
				res = session.m_argumentExprs[0][0];
			res = session.m_iteratorVarExpr; //todo: was m_exprDollarVar, thus rename in .h
		}
		else if (parseWord("$$")) {
				res=getArgumentListExpr();
		} 
		else if (parseDigits(m_s,i)>0) {
			if (i==0) throw("Variable $0 is not valid, use: $,$2,$3...");
			nextWord();
			if (i<2 && session.m_funcLevel<4)
				res = session.m_argumentExprs[session.m_funcLevel][i];
			else
				new (res) ArgumentExpr(i,session.m_funcLevel);
		}
		else if (parseWord("$#")) {
			res = session.m_iteratorKeyVarExpr;
		}
	}
	else {
		bool localFind = parseWord(".");
		unsigned char c=m_s[0];
		if ( isIdentifier(c) ) {
			res = session.findOrInsert((char *)m_s,localFind);
			nextWord();
		}
		else if (localFind)
			throw("Variable expected after '.'.");
	}
	return res;
}

int TyleParser::parseDigits(const unsigned char *p, /*out */int &result, int base)
{
	result=0;
	int count=0;
	for(;;)
	{ int v = hexDigitToValue(*p);
	  if (v<0 || v>=base) break;
	  result = result * base + v;
		count++;
		p++;
	}
	return count;
}

const unsigned char *TyleParser::parseInt(const unsigned char *p, int &base, /*out*/ int &result)
{	
	int sign=1;
	if (*p=='-') { p++; sign=-1; }
	if (*p<'0' || *p>'9') { result=0; return 0; }
	if (*p=='0' && p[1]!=0 && p[1]!='.' && (p[1]<'0' || p[1]>'9'))
	{
		if (p[1]=='x' || p[1]=='X')      { p+=2; base=16; }
		else if (p[1]=='o' || p[1]=='O') { p+=2; base=8; }
		else if (p[1]=='b' || p[1]=='B') { p+=2; base=2; }
		else { result=0; return 0; }
	} else base=10;
	p+=parseDigits(p,result,base);
	result*=sign;
	return p;
}
	
EPtr TyleParser::parseNumber() {
	int result;
	int base;
	EPtr res;
	const unsigned char *p=m_s;
	p=parseInt(p, base, result);
	if (!p)
		return EPtr();
	int r2=0,c2=0,r3=0, isFloat=0;
	if (*p=='.')
	{	if ((c2=parseDigits(++p,r2,base))==0) { throw("Missing digits after '.'"); return EPtr(); }
		p+=c2;
		isFloat=1;
	}
	if (*p=='e'||*p=='E')
	{	int esign=1;
		if (p[1]=='-') { esign=-1; p++; }
		else if (p[1]=='+') p++;
		parseDigits(p+1,r3,10);
		r3*=esign;
		isFloat=1;
	}
	if (isFloat)
		NEW_CONSTEXPR(res) FloatObject((result + r2*pow(base,-c2))*pow(10,r3) );
	else
	{	if (result>=0 && result<4)
		res=session.m_exprNumbers[result];
	else
		NEW_CONSTEXPR(res) IntegerObject(result);
	}
		
	nextWord();
	return res;
}

void TyleParser::doConsolidateSharedLiterals(EPtr &e) {
	bool oldInARef = m_sharedLiteralConsolidation.inARef;
	if (e->isContainer(m_sharedLiteralConsolidation.inARef)) {
		e->iterate(doConsolidateSharedLiteralsCallback, (void *)this);
		//todo later: improve so that { {#1=..,&#1}, x+y} is also possible
	} else {
		if (!e->consolidateSharedLiterals(m_sharedLiteralConsolidation.sharedLiterals,m_sharedLiteralConsolidation.inARef)) { //only applies to sharedLiteral and assignedSharedLiteral
			e->iterate(consolidateSharedLiteralsCallback, (void *)this);
			m_sharedLiteralConsolidation.isDynamic=true;
		}
	}
	m_sharedLiteralConsolidation.inARef = oldInARef;
}

void TyleParser::doConsolidateSharedLiteralsCallback(EPtr &e, void *self) {
	((TyleParser *)self)->doConsolidateSharedLiterals(e);
}

void TyleParser::consolidateSharedLiteralsCallback(EPtr &e, void *self) {
	((TyleParser *)self)->consolidateSharedLiterals(e);
}

void TyleParser::consolidateSharedLiterals(EPtr &e) {
	//vector<EPtr> sharedLiterals;
	m_sharedLiteralConsolidation.inARef = false;
	m_sharedLiteralConsolidation.isDynamic = false;
	doConsolidateSharedLiterals(e);
	if (!m_sharedLiteralConsolidation.sharedLiterals.empty()) {
		if (m_sharedLiteralConsolidation.isDynamic) {
			throw("#[num] can only be used in a complete literal expression");
		}
		EPtr r;
		new (r) SharedLiteralsScopeExpr(e,m_sharedLiteralConsolidation.sharedLiterals);
		e = r;
	}
}

//====================
void TyleParser::doConstify(EPtr &e) {
	bool dummy;
	_ASSERT(!!e);
	if (e->isContainer(dummy)) {
		bool oldIsConst = m_constification.isConst;
		m_constification.isConst = true;
		e->iterate(doConstifyCallback, (void *)this);
		if (!m_constification.isConst) // so it is not const, but it means that some elements might be const that need constification
			e->iterate(constifyCallback, (void *)this);
		m_constification.isConst &= oldIsConst;
	}
	else if (e->isDynamic()) {
		m_constification.isConst = false;
		e->iterate(constifyCallback, (void *)this);
	}
}

void TyleParser::doConstifyCallback(EPtr &e, void *self) {
	((TyleParser *)self)->doConstify(e);
}

void TyleParser::constifyCallback(EPtr &e, void *self) {
	((TyleParser *)self)->constify(e);
}

void TyleParser::constify(EPtr &e) {
	//vector<EPtr> sharedLiterals;
	m_constification.isConst = true;
	doConstify(e);
	if (m_constification.isConst) {
		EPtr r;
		r = e->getPredefined();
		if (!r)
			new (r) ConstExpr(e->evaluateToPersistCopy());
		e = r;
	}
}

EPtr TyleParser::parseHash() {
	EPtr res;
	if (m_s[0]=='#') {	
		if (m_s[1]>='1' && m_s[1]<='9') {
			unsigned int value;
			parseDigits((const unsigned char *)m_s+1,(int &)value,10);
			value--;
			nextWord();
			if (parseWord("=")) {
				EPtr o=parseUnary();
				pcheck(o);
				new (res) SharedLiteralExprAssigned(value,o); 
			} else {
				new (res) SharedLiteralExpr(value); 
			}
		}
		else {
			throw("Expected number after #");
		}
	}
	return res;
}

EPtr TyleParser::parseOperandOrMultiStat(bool allowStatement) {
	EPtr res, psd (parseTypeSelectionExpr() );

	if (!(res=parseSubExpressionOrMultiStat(allowStatement)))
	if (!(res=parseString()))
	if (!(res=parseNumber()))
	if (!(res=parseVar()))
		res=parseHash();

	if (!!psd) {
		((TypeSelectionExpr *)&*psd)->a=res;
		return psd;
	}
	else return res;
	
}

//#define RetObj(x) { EPtr res; new (res) x; return res; }

#define ENDFUNC }
#define BINARY_BEGIN(child) \
	{ EPtr cur(child); \
	if ( !cur ) return EPtr();\
	while (true) {
#define BINARY_END \
		else break; \
	} return cur; \
}
#define BINARY_BEGIN_RIGHT(x) \
	{ EPtr cur(x); \
	if ( !cur ) return EPtr();
#define BINARY_END_RIGHT \
	return cur;\
}
#define CREATE_BIN_ITEM(x,child) { EPtr prev(cur); EPtr f(child()); pcheck(f); new (cur) x(prev,f); }
#define CREATE_SUB_BIN_ITEM(x,child) { session.m_funcLevel++; CREATE_BIN_ITEM(x,child); session.m_funcLevel--;}	
#define NEXT_CREATE_BIN_ITEM(x,child) { nextWord(); CREATE_BIN_ITEM(x,child); }

#define BINARY_ITEM(str,x,child) if (parseWord(str)) CREATE_BIN_ITEM(x,child)

EPtr TyleParser::parseIndexOrMultiStat(bool allowStatement) {
BINARY_BEGIN(parseOperandOrMultiStat(allowStatement))
	bool isBracket=parseWord("[");
	if ( isBracket || parseWord("[:") ) { 
		pcheck(cur);
		EPtr prev(cur);
		bool isAlt=!isBracket;
		EPtr e( parseExpr() );
		pcheck(e);
		if (isAlt)
			new (cur) IndexAltExpr(prev,e);
		else
			new (cur) IndexExpr(prev,e);
		if (!parseWord("]")) throw("Missing ']'.");
	}
	else if ( parseWord("<>") ) { 
		pcheck(cur);
		EPtr prev(cur);
		new (cur) StructNameExpr(prev);
	}
	else if ( parseWord("!") ) { 
		pcheck(cur);
		EPtr prev(cur);
		new (cur) ApplyExpr(prev,getArgumentListExpr());
	}
	else if ( parseWord(".*") ) { 
		pcheck(cur);
		EPtr prev(cur);
		new (cur) StructElemsExpr(prev);
	}
	else  {	
		bool di=parseWord(".@");
		if (di || parseWord(".") ) {
			pcheck(cur);
			EPtr prev(cur);
			unsigned char c=m_s[0];
			if ( isIdentifier(c) ) { 
				String name((char *)m_s);
				nextWord();
				if (di) new (cur) StructIndexingNumberExpr(prev, name);
				else new (cur) StructIndexingExpr(prev,name);
			} else if (di) 
				new (cur) StructLengthExpr(prev);
			else throw("Unexpected after '.'");
		}
		else if (parseWord("(")) {
			pcheck(cur);
            EPtr res(finishParseArguments());
			EPtr prev(cur);
			new (cur) ApplyExpr(res, prev);
		}
	}
}
return cur;
}
ENDFUNC

//todo: this must all change as soon as real compiler comes, because then the parse tree is just stored.
EPtr TyleParser::parseUnaryOrMultiStat(bool allowStatement) {
#define UNARY_ITEM(i,x) case i: nextWord(); pcheck(f=parseUnary()); new (r) x##Expr(f); break;
	EPtr f,r;
	if(m_s[1]==0)
	{
		switch(m_s[0])
		{
		case '&': 
			{
				nextWord(); pcheck(f=parseUnary()); new (r) ReferenceExpr(f); 
				break;
			}
		UNARY_ITEM('!',Inverse    )
		UNARY_ITEM('-',Negate     )
		UNARY_ITEM('@',Length     )
		UNARY_ITEM('*',DeReference)
		UNARY_ITEM('~',BitNot     )
		default: return parseIndexOrMultiStat(allowStatement);
		}
	}
	else if (parseWord("@@")) { pcheck(f=parseUnary()); new (r) TypeExpr(f); }
	else if (parseWord("*!")) { pcheck(f=parseUnary()); new (r) ValueExpr(f); }
	else return parseIndexOrMultiStat(allowStatement);
	return r;
ENDFUNC

EPtr TyleParser::parseBinaryOrMultiStat(bool allowStatement, int level)
{
	EPtr cur(parseUnaryOrMultiStat(allowStatement));
	if ( !cur ) return EPtr();
	return parseBinaryOp(cur,level);
}

//todo: checkExpr includes pcheck
#define BINITEM(x) \
{ \
	if (level##x<level) \
	{ \
		nextWord(); \
		EPtr prev(cur); EPtr f(parseBinaryOrMultiStat(false, level##x)); pcheck(f); new (cur) x##Expr(prev,f); \
		break; \
	} \
}

EPtr TyleParser::parseBinaryOp(EPtr cur, int level)
{
	while (true)
	{
		switch(m_s[0])
		{
		case '&':
			if(m_s[1]==0) BINITEM(BitAnd) else if (m_s[1]=='&' && m_s[2]==0) BINITEM(And) 
			return cur;
		case '|':
			if(m_s[1]==0) BINITEM(BitOr) else if (m_s[1]=='|' && m_s[2]==0) BINITEM(Or)
			return cur;
		case '^': 
			if (m_s[1]==0) BINITEM(BitXor) 
			return cur; 
		case '=': 
			if (m_s[1]=='=' && m_s[2]==0) BINITEM(Equal) 
			return cur; 
		case '!': 
			if (m_s[1]=='=' && m_s[2]==0) BINITEM(Unequal)
			return cur; 
		case '<': 
			if (m_s[1]==0) BINITEM(Smaller) else if (m_s[2]==0) 
			{ if (m_s[1]=='=') BINITEM(SmallerEqual)
				else if (m_s[1]=='<' ) BINITEM(ShiftLeft)
			}
			return cur; 
		case '>': 
			if (m_s[1]==0) BINITEM(Greater) else if (m_s[2]==0) 
			{ if (m_s[1]=='=') BINITEM(GreaterEqual)
				else if (m_s[1]=='>') BINITEM(ShiftRight)
			}
			return cur; 
		case '+': 
			if (m_s[1]==0) BINITEM(Add) else if (m_s[2]==0) 
			{ if (m_s[1]=='>') BINITEM(Add) // deprecated
				else if (m_s[1]=='<') BINITEM(Prepend)
			}
			return cur; 
		case '-': 
			if (m_s[1]==0) BINITEM(Sub)
			return cur; 
		case '*': 
			if (m_s[1]==0) BINITEM(Mul)
			return cur; 
		case '/': 
			if (m_s[1]==0) BINITEM(Div)
			return cur; 
		case '%': 
			if (m_s[1]==0) BINITEM(Mod)
			return cur; 
		case '@': 
			if (m_s[1]==0) BINITEM(FindIndex)
			if (m_s[2]==0) {
				if (m_s[1]=='+') BINITEM(InsertAt)
				else if (m_s[1]=='-') BINITEM(RemoveAt)
			}
			return cur; 
		default: return cur;
		}
	}
}
#undef BINITEM

EPtr TyleParser::parseSwitchOrMultiStat(bool allowStatement) {
BINARY_BEGIN_RIGHT(parseBinaryOrMultiStat(allowStatement))
	if (parseWord("?")) {
		pcheck(cur);
		//todo designators: <1>, <1,2,3>
		EList l;
		EList::P lp(&l);
		do { 
			EPtr r=parseSubStatement();
			lp->prepend(r); lp++;
		} while (parseWord(":"));
		EPtr prev=cur;
		new (cur) SwitchOnExpr(prev, l );
	}
BINARY_END_RIGHT
ENDFUNC

EPtr TyleParser::parseFunction() {
	EPtr res;
	int pushes=0;
	try {
		if(parseWord("|")) {
			List<TypeFunc> structFuncs;
			structing.funcs=&structFuncs;
			if ( session.m_funcLevel ) { // ( i==0 && (!!subFunc || !!subType)) || ( i==1 && !commaCame ) )
				TypeFunc *func = 0, *keyFunc=0;
				TypeVar type;
				String id, keyId;
				for(int i=0;;i++) {
					type = parseTypeSelection(func);
					if (!isIdentifier(m_s[0])) { 
						delete func;
						throw("Expected identifier.");
					}
					id=m_s;
					if (!!type)
						func = new TypeSelection(type);
					nextWord();
					if (i==1 || !parseWord(":"))
						break;
					keyFunc = func;
					keyId = id;
				}
				if (!parseWord("|"))
					throw("Expected '|'");
				EPtr theChild=parseProcedure();
				if (!theChild)
					throw("Expected expression after parameter definition");
				NamedIteratorVarsScope::Ptr scope;
				new (scope) NamedIteratorVarsScope(id, keyId, session.m_funcLevel);
				session.push(scope); pushes++;
				new (res) NamedIteratorVarAssignmentExpr( func, keyFunc, structFuncs, scope, theChild );
			}
			else
			{	
				List<StructElem> ses;
				int i=0;
				TypeVar tp;
				new (tp) TypeVal();
				if (!parseWord("|")) { 
					for(;;) {
						TypeFunc *subFunc = 0;
						TypeVar subType;
						subType = parseTypeSelection(subFunc);
						if ( (!!subFunc || !!subType) && i==0 && parseWord("|")) 
							break;
						if (!isIdentifier(m_s[0])) { 
							delete subFunc;
							throw("Expected identifier.");
						}
						String m_sc((char *)m_s);
						addStructElem(ses,tp,m_sc,subType,subFunc);i++;
						nextWord();
						if (parseWord("|")) break;
						if (session.m_funcLevel && !parseWord(","))
							throw("Expected ',' or '|'.");
						else if (!session.m_funcLevel && !parseWord(":"))
							throw("Expected ':' or '|'.");
					}
				}
				EPtr theChild=parseProcedure();
				if (!theChild)
					throw("Expected expression after parameter definition");
				tp->set(ses,false);
				ParamListScope::Ptr scope;
				new (scope) ParamListScope(tp);
				session.push(scope); pushes++;
				new (res) ParamListAssignmentExpr( structFuncs, scope, theChild );
			}
		} else {
			res=parseProcedure();
			if (!res)
				throw("Expected function content expression");
		}
		while(--pushes>=0) session.pop();
	} catch(const char *error) {
		while(--pushes>=0) session.pop();
		throw(error);
	}
	return res;
ENDFUNC

// right recursive:
EPtr TyleParser::parseChainBlockOrMultiStat(bool allowStatement) {
	if (parseWord("~:")) {
		EPtr cur; //just null pointer
		CREATE_SUB_BIN_ITEM(ActOnMultiExpr,parseFunction)
		return cur;
	} else {
		BINARY_BEGIN_RIGHT(parseSwitchOrMultiStat(allowStatement))
			if (parseWord("~"))
				CREATE_SUB_BIN_ITEM(ActOnExpr,parseFunction)
			else if (parseWord("~:"))
				CREATE_SUB_BIN_ITEM(ActOnMultiExpr,parseFunction)
			else if (parseWord("~%"))
				CREATE_SUB_BIN_ITEM(ActOnColumnsExpr,parseFunction)
			else if (!cur->isExpr() && !allowStatement)
				throw("Expected expression instead of statement");
		BINARY_END_RIGHT
	}
ENDFUNC

// unary
EPtr TyleParser::parseFunctionLiteral() {
	EPtr res;
	int level=0;
	while (parseWord("?")) level++;
	if (level)
	{	int ol=session.m_funcLevel; session.m_funcLevel=0; 
		ClosureScope::Ptr scope;
		new (scope) ClosureScope(ol);
		session.push(scope);
		EPtr f;
		try {
			pcheck(f=parseFunction());  
		} catch(const char *error) {
			session.pop();
			throw(error);
		}
		session.pop();
		if (((ClosureScope &)*scope).m_count) {	
			new (res) ClosuredExprExpr(f, scope);
		}
		else { 
			NEW_CONSTEXPR(res) ExprObject(f);
		}
		while(--level>0) {
			EPtr prev(res);
			NEW_CONSTEXPR(res) ExprObject(prev);
		}
		
		session.m_funcLevel=ol; 
	}
	return res;
ENDFUNC

//todo:
//	else if (parseWord(".?")) { pcheck(f=parseModify()); new (r) DescribeExpr(f); }

// right recursive:
EPtr TyleParser::parseExprOrMultiStat(bool allowStatements) {
	EPtr cur(parseFunctionLiteral());
	if (!!cur)
		return cur;
	cur = parseChainBlockOrMultiStat(allowStatements);
	if (!cur->isLValue())
		return cur;
	if (parseWord("=")) {
		CREATE_BIN_ITEM(AssignmentExpr,parseExpr); 
	}
	else if (m_s[1]=='=' && m_s[2]==0) {
		switch(m_s[0])
		{
		case '*': NEXT_CREATE_BIN_ITEM(MulAssignExpr,parseExpr); break;
		case '/': NEXT_CREATE_BIN_ITEM(DivAssignExpr,parseExpr); break;
		case '%': NEXT_CREATE_BIN_ITEM(ModAssignExpr,parseExpr); break;
		case '+': NEXT_CREATE_BIN_ITEM(AddAssignExpr,parseExpr); break;
		case '-': NEXT_CREATE_BIN_ITEM(SubAssignExpr,parseExpr); break;
		case '|': NEXT_CREATE_BIN_ITEM(BitOrAssignExpr,parseExpr);  break;
		case '&': NEXT_CREATE_BIN_ITEM(BitAndAssignExpr,parseExpr); break;
		case '^': NEXT_CREATE_BIN_ITEM(BitXorAssignExpr,parseExpr); break;
		default: break;
		}
	}
	else if (m_s[2]=='=' && m_s[3]==0) {
		if     ( m_s[0]=='>' && m_s[1]=='>' ) { NEXT_CREATE_BIN_ITEM(ShiftRightAssignExpr, parseExpr ); }
		else if( m_s[0]=='<' && m_s[1]=='<' ) { NEXT_CREATE_BIN_ITEM(ShiftLeftAssignExpr, parseExpr ); }
		else if( m_s[0]=='+' && m_s[1]=='<' ) { NEXT_CREATE_BIN_ITEM(PrependAssignExpr, parseExpr ); }
		else if( m_s[0]=='@' && m_s[1]=='+' ) { NEXT_CREATE_BIN_ITEM(InsertAtAssignExpr, parseExpr ); }
		else if( m_s[0]=='@' && m_s[1]=='-' ) { NEXT_CREATE_BIN_ITEM(RemoveAtAssignExpr, parseExpr ); }
		else if( m_s[0]=='&' && m_s[1]=='&' ) { NEXT_CREATE_BIN_ITEM(AndAssignExpr, parseExpr ); }
		else if( m_s[0]=='|' && m_s[1]=='|' ) { NEXT_CREATE_BIN_ITEM(OrAssignExpr, parseExpr ); }
	}
	return cur;
ENDFUNC

void TyleParser::lValueCheck(EPtr e) {
	if (!e->isLValue())
		throw("Expected lvalue in assignment");
}

EPtr TyleParser::finishParseList()
{ 
	EPtr res;
	try
	{
		EList elements;
		EList::P ep(&elements);
		if (!parseWord("]")) {
			for(;;) {
				EPtr j=parseExpr();
				if (!j) throw("Expected expression after '[', or ','.");
				ep->prepend(j);ep++;
				if ( parseWord("]")) break;
				if ( !parseWord(","))
					throw("Expected ',', or ']'.");
			}
		}
		new (res) ListExpr(elements);
	} catch(const char *error) {
		throw(error);
	}
	return res;
}

EPtr TyleParser::finishParseSequence()
{ 
	EPtr res;
	EList elements;
	EList::P ep(&elements);
	if (!parseWord(")")) {
		for(;;) {
			EPtr j=parseExpr();
			if (!j) throw("Expected expression after '(', or ','.");
			ep->prepend(j);ep++;
			if ( parseWord(")")) break;
			if ( !parseWord(","))
				throw("Expected ',', or ')'.");
		}
	}
	else
		return session.m_exprNull;
	if ( elements.length()==1 )
		res = *elements;
	else {
		new (res) SequenceExpr(elements);
	}
	return res;
}

EPtr TyleParser::finishParseArguments()
{ 
	EPtr res;
	EList elements;
	EList::P ep(&elements);
	if (!parseWord(")")) {
		for(;;) {
			bool squashed=parseWord("=");
			EPtr j=parseExpr();
			if (!j) throw("Expected expression after '(', ',', or '='.");
			if (squashed) {
				if (!!elements) {
					if (!!res) {
						EPtr prev(res);
						EPtr elems;
						new (elems) ListExpr(elements);
						new (res) AddExpr(prev,elems);
					}
					ep=&elements;
				}
				if (!!res) {
					EPtr prev(res);
					new (res) AddExpr(prev,j);
					
				} else
					res = j;
			}
			ep->prepend(j);ep++;
			if ( parseWord(")")) break;
			if ( !parseWord(","))
				throw("Expected ',', or ')'.");
		}
	}
	else
		return session.m_exprNull;
	if ( elements.length()==1 && !!res)
		res = *elements;
	else if (!!res) {
		if (!!elements) {
			EPtr prev(res);
			EPtr elems;
			new (elems) ListExpr(elements);
			new (res) AddExpr(prev,elems);
		}
	}
	else
		new (res) SequenceExpr(elements);
	return res;
}

EPtr TyleParser::parseProgram() { 
	LocalVarVectorQueue::Ptr queue;
	new (queue) LocalVarVectorQueue();
	session.push(queue);
	EPtr res;
	EList elements;
	EList::P ep(&elements);
	for(;;) {
		EPtr j=parseSubStatement();
		if (!j) break;
		ep->prepend(j);ep++;
		if ( !parseWord(";")) break;
	}
	if ( !session.lvvqPop() && elements.length()==1 ) { 
		res = *elements; //todo: what happens when it is a single non-expression?
	}
	else { //StatementList:
		new (res) MultiStat(elements, queue);
	}
	return res;
}

EPtr TyleParser::parseArguments()
{ 
	if (parseWord("(")) {
		EPtr res;
		try
		{
			// todo: .name = ... initializers.
			EList elements;
			EList::P ep(&elements);
			if (!parseWord(")")) {
				for(;;) {
					EPtr j=parseExpr();
					if (!j) throw("Expected expression after '(', or ','.");
					ep->prepend(j);ep++;
					if ( parseWord(")")) break;
					if ( !parseWord(","))
						throw("Expected ',', or ')'.");
				}
			}
			new (res) ListExpr(elements);
		} catch(const char *error) {
			throw(error);
		}
		return res;
	}
	return EPtr();
}
/*todo
EPtr TyleParser::finishMultiStatOrMap(bool mapOnly)
{
	EPtr cur(finishMultiStatOrMap(mapOnly));
	if (cur->isExpr())
		return cur;
}
*/
EPtr TyleParser::finishMultiStatOrMap(bool mapOnly)
{
	_TVAR(i,mapOnly?1:0);
	EPtr res;
	int type=mapOnly?2:0; // 1==MultiStat, 2==map

	EList elements;
	EList::P lp(&elements);

	LocalVarVectorQueue::Ptr queue;
	new (queue) LocalVarVectorQueue();
	session.push(queue);
	if (parseWord("}"))
		type = 2;
	else {
		for(;;) { 
			EPtr a(type==2 ? parseExpr() : parseSubStatement());
			if (!a)
				throw("Expected statement or '}'");
			lp->prepend(a);lp++;
			if (type==0 && !a->isExpr())
				type=1;
			EPtr b;
			if ( parseWord(":")) {
				if (type==1)
					throw("No ':' allowed in a compound statement { ...;...; }");
				type=2;
				b=parseExpr();
				if (!b) throw("Expected an expression");
			}  
			if ( parseWord(";")) {
				if (type==2)
					throw("No ';' allowed in a map literal");
				type=1;
			} else if (type==1)
				throw("Expected ';' in a compound statement { ...;...; }");
			else
				type=2;
			if (type==2) {
				lp->prepend(b);lp++;
			}
			if ( parseWord("}")) break;
			if ( parseWord(",")) {
				if (type==1)
					throw("No ',' in a compound statement { ...;...; }");
			} else if (type==2)
				throw("Expected ',' in a map literal");
		}
	}
	session.lvvqPop();
	if (type==1) {
		new (res) MultiStat(elements, queue);
	} else {
		if (!queue->isEmpty())
			throw ("cannot define local variables inside a map literal");
		new (res) MapExpr(elements);
	}
	return res;
}

EPtr TyleParser::parseProcedure() {
	if (parseWord("{")) return finishMultiStatOrMap(false);
	else return parseExpr();
ENDFUNC

EPtr TyleParser::parseSubExpressionOrMultiStat(bool allowStatement) {
	EPtr res;
	if (parseWord("{")) res = finishMultiStatOrMap(allowStatement);
	else if (parseWord("[")) res = finishParseList();
	else if (parseWord("(")) res = finishParseSequence();
	return res;
ENDFUNC

EPtr TyleParser::parseCatchBlock() {
BINARY_BEGIN(parseTopStatement())
	if (parseWord("<~~")) {
		CREATE_BIN_ITEM(FinallyExpr,parseFunction); 
	}
	else if (parseWord("<!~")) {
		CREATE_BIN_ITEM(CatchExpr,parseFunction); 
	}
BINARY_END
ENDFUNC

EPtr TyleParser::parseSubStatement() {
	EPtr res(parseCatchBlock());
	if (!!res)
		return res;
	Session::FlowState s=
		parseWord("=>")?Session::flow_continueOrReturn:(
			parseWord("=>>")?Session::flow_break:(
				parseWord("=>>>")?Session::flow_return:Session::flow_run
			)
		);
	if(s==Session::flow_run)
		return parseExprOrMultiStat(true);
	EPtr e(parseExpr()), r;
	if (!e)
		new (r) FlowChangerWithoutResultStatement(s);
	else
		new (r) FlowChangerWithResultStatement(s,e);
	return r;
ENDFUNC

EPtr TyleParser::parseTopStatement() {
	if (parseWord("=!>")) {
		EPtr e(parseExpr());
		pcheck(e);
		EPtr res;
		new (res) FlowChangerWithResultStatement(Session::flow_exception,e);
		return res;
	}
	EPtr res(parseExprOrMultiStat(true));
	if (!!res)
		return res;
ENDFUNC
